package com.anrong.sdk;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.anrong.sdk.callback.InitCallBack;
import com.anrong.sdk.callback.ZipCallBack;
import com.anrong.sdk.util.log.LogUtil;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.progress.ProgressMonitor;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;

/**
 * 解压工具类
 * 
 * @author LangK
 * 
 */
public class UnZipUtil {

	private static final String TAG = "UnZipUtil";

	/**
	 * 解压
	 * 
	 * @param zipFile
	 *            源文件
	 * @param filePath
	 *            解压文件路径
	 * @param handler
	 *            进度回调
	 * @param isDeleteZip
	 *            是否删除源文件
	 * @param password
	 *            解压密码
	 * @param isEncryption
	 *            文件是否加密
	 * @throws ZipException
	 */
	private static void unZip(final File zipFile, String filePath,
			final Handler handler, final boolean isDeleteZip, String password,
			boolean isEncryption) throws ZipException {
		if (isEncryption) {
			final File decodeFile = new File(zipFile.getParentFile(), "test");
			try {
				// 解密
				decodeFile(zipFile, decodeFile, true);
			} catch (Exception e) {
				e.printStackTrace();
			}

			Log.d(TAG, "开始解压");
			ZipFile zFile = new ZipFile(decodeFile);
			zFile.setFileNameCharset("GBK");

			if (!zFile.isValidZipFile()) {
				throw new ZipException("exception!");
			}
			File destDir = new File(filePath); // 解压目录文件
			if (destDir.isDirectory() && !destDir.exists()) {
				destDir.mkdir();
			}
			if (zFile.isEncrypted()) {
				zFile.setPassword(password); // 设置解压密码
			}

			final ProgressMonitor progressMonitor = zFile.getProgressMonitor();
			Thread thread = new Thread(new Runnable() {
				@Override
				public void run() {
					Bundle bundle = null;
					Message msg = null;
					try {
						int precentDone = 0;
						if (handler == null) {
							return;
						}
						handler.sendEmptyMessage(CompressStatus.START);
						while (true) {
							// 每隔50ms,发送一个解压进度出去
							Thread.sleep(50);
							precentDone = progressMonitor.getPercentDone();
							System.out.println(precentDone);
							bundle = new Bundle();
							bundle.putInt(CompressStatus.PERCENT, precentDone);
							msg = new Message();
							msg.what = CompressStatus.HANDLING;
							msg.setData(bundle);
							handler.sendMessage(msg); // 通过 Handler将进度扔出去
							if (precentDone >= 100) {
								break;
							}
						}
						handler.sendEmptyMessage(CompressStatus.COMPLETED);
					} catch (InterruptedException e) {
						bundle = new Bundle();
						bundle.putString(CompressStatus.ERROR_COM,
								e.getMessage());
						msg = new Message();
						msg.what = CompressStatus.ERROR;
						msg.setData(bundle);
						handler.sendMessage(msg);
						e.printStackTrace();
					} finally {
						if (isDeleteZip) {
							decodeFile.delete();// 将原压缩文件删除
						}
					}
				}
			});
			thread.start();
			zFile.setRunInThread(true); // true 在子线程中进行解压 , false主线程中解压
			zFile.extractAll(filePath); // 将压缩文件解压到filePath中...
		} else {
			Log.d(TAG, "开始解压");
			ZipFile zFile = new ZipFile(zipFile);
			zFile.setFileNameCharset("GBK");

			if (!zFile.isValidZipFile()) {
				throw new ZipException("exception!");
			}
			File destDir = new File(filePath); // 解压目录文件
			if (destDir.isDirectory() && !destDir.exists()) {
				destDir.mkdir();
			}
			if (zFile.isEncrypted()) {
				zFile.setPassword(password); // 设置解压密码
			}

			final ProgressMonitor progressMonitor = zFile.getProgressMonitor();
			Thread thread = new Thread(new Runnable() {
				@Override
				public void run() {
					Bundle bundle = null;
					Message msg = null;
					try {
						int precentDone = 0;
						if (handler == null) {
							return;
						}
						handler.sendEmptyMessage(CompressStatus.START);
						while (true) {
							// 每隔50ms,发送一个解压进度出去
							Thread.sleep(50);
							precentDone = progressMonitor.getPercentDone();
							System.out.println(precentDone);
							bundle = new Bundle();
							bundle.putInt(CompressStatus.PERCENT, precentDone);
							msg = new Message();
							msg.what = CompressStatus.HANDLING;
							msg.setData(bundle);
							handler.sendMessage(msg); // 通过 Handler将进度扔出去
							if (precentDone >= 100) {
								break;
							}
						}
						handler.sendEmptyMessage(CompressStatus.COMPLETED);
					} catch (InterruptedException e) {
						bundle = new Bundle();
						bundle.putString(CompressStatus.ERROR_COM,
								e.getMessage());
						msg = new Message();
						msg.what = CompressStatus.ERROR;
						msg.setData(bundle);
						handler.sendMessage(msg);
						e.printStackTrace();
					} finally {
						if (isDeleteZip) {
							zipFile.delete();// 将原压缩文件删除
						}
					}
				}
			});
			thread.start();
			zFile.setRunInThread(true); // true 在子线程中进行解压 , false主线程中解压
			zFile.extractAll(filePath); // 将压缩文件解压到filePath中...

		}

	}

	/**
	 * 无密码解压
	 * 
	 * @param zipFile
	 *            源文件
	 * @param filePath
	 *            解压文件路径
	 * @param handler
	 *            进度回调
	 * @param isDeleteZip
	 *            是否删除源文件
	 * @param isEncryption
	 *            文件是否加密
	 * @throws ZipException
	 */
	public void unZip(final File zipFile, String filePath,
			final Handler handler, final boolean isDeleteZip,
			boolean isEncryption) throws ZipException {
		unZip(zipFile, filePath, handler, isDeleteZip, "", isEncryption);
	}

	/**
	 * 解压 无密码、删除源解压文件
	 * 
	 * @param context
	 * @param zipFile
	 *            源文件
	 * @param filePath
	 *            解压文件路径
	 * @param handler
	 *            进度回调
	 * @param isEncryption
	 *            文件是否加密
	 * @throws ZipException
	 */
	public static void unZip(final File zipFile, String filePath,
			final Handler handler, boolean isEncryption) throws ZipException {
		unZip(zipFile, filePath, handler, true, "", isEncryption);
	}

	/**
	 * @throws ZipException
	 *             解压缩
	 * 
	 * @Title: upZip
	 * @Description: (这里用一句话描述这个方法的作用)
	 * @param @param zipFile
	 * @param @param filePath
	 * @param @param callBack
	 * @return void 返回类型
	 * @throws
	 */
	public static void upZip(final Activity activity, final File zipFile,
			final String filePath, final ZipCallBack callBack)
			throws ZipException {
		LogUtil.trace(Log.DEBUG, TAG, "开始解压");
		// 先显示对话框
		// 删除过期文件
		// 异步解压缩
		// 计算文件大小
		// 显示进度
		callBack.onPrepare();
		final ZipFile zFile = new ZipFile(zipFile);
		if (!zFile.isValidZipFile()) {
			callBack.onError(
					ZipCallBack.CODE_EXPLODE_PACKET_NOTEXIST_EXCEPTION,
					ZipCallBack.MSG_EXPLODE_PACKET_NOTEXIST);
			return;
		}
		new Thread(new Runnable() {

			@Override
			public void run() {
				File destDir = new File(filePath); // 解压目录文件
				// 先删除过期文件
				deleteDirectory(filePath);
				// 删除log日志
				deleteDirectory(LogUtil.getSaveDir());
				destDir = new File(filePath);
				if (destDir.isDirectory() && !destDir.exists()) {
					destDir.mkdir();
				}
				//
				// 计算zip文件大小
				long start = System.currentTimeMillis();
				final long maxSize = getZipFileSize(zipFile);
				LogUtil.trace(Log.INFO, TAG,
						"计算时间：" + (System.currentTimeMillis() - start) / 1000);
				// 计算完成
				activity.runOnUiThread(new Runnable() {

					@Override
					public void run() {
						callBack.onReckonSizeFinished(maxSize);
					}
				});
				// 显示进度
				new Thread(new Runnable() {
					@Override
					public void run() {
						final ProgressMonitor progressMonitor = zFile
								.getProgressMonitor();
						long currentSize = 0;
						progressMonitor.setTotalWork(maxSize);
						while (true) {
							currentSize = progressMonitor.getWorkCompleted();
							// 每隔50ms,发送一个解压进度出去
							try {
								Thread.sleep(500);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							int progress = (int) (((float) currentSize / maxSize) * 100);
							if (progress >= 99) {
								progress = 99;
							}
							// 有可能大于最大大小
							if (currentSize >= maxSize) {
								SystemClock.sleep(5000);
								zipFile.delete();
								// zipFile.deleteOnExit();// 将原压缩文件删除
								activity.runOnUiThread(new Runnable() {
									@Override
									public void run() {
										callBack.onSuccess();
									}
								});
								break;
							}
							callBack.onProgress(progress, currentSize, maxSize,
									CompressStatus.PERCENT);
						}
					}
				}).start();
				// 异步解压缩
				zFile.setRunInThread(false); // true 在子线程中进行解压 , false主线程中解压
				try {
					zFile.extractAll(filePath);
				} catch (ZipException e) {
					e.printStackTrace();
				} // 将压缩文件解压到filePath中

			}
		}).start();

	}

	private static boolean deleteDirectory(String sPath) {
		if (sPath == null || sPath.equals("")) {
			LogUtil.trace(Log.WARN, TAG, "删除路径不存在");
			return false;
		}
		// 如果sPath不以文件分隔符结尾，自动添加文件分隔符
		if (!sPath.endsWith(File.separator)) {
			sPath = sPath + File.separator;
		}
		File dirFile = new File(sPath);
		// 如果dir对应的文件不存在，或者不是一个目录，则退出
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return false;
		}
		boolean flag = true;
		// 删除文件夹下的所有文件(包括子目录)
		File[] files = dirFile.listFiles();
		for (int i = 0; i < files.length; i++) {
			// 删除子文件
			if (files[i].isFile()) {
				flag = deleteFile(files[i].getAbsolutePath());
				if (!flag)
					break;
			} // 删除子目录
			else {
				flag = deleteDirectory(files[i].getAbsolutePath());
				if (!flag)
					break;
			}
		}
		if (!flag)
			return false;
		// 删除当前目录
		if (dirFile.delete()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 删除单个文件
	 * 
	 * @param sPath
	 *            被删除文件的文件名
	 * @return 单个文件删除成功返回true，否则返回false
	 */
	private static boolean deleteFile(String sPath) {
		boolean flag = false;
		File file = new File(sPath);
		// 路径为文件且不为空则进行删除
		if (file.isFile() && file.exists()) {
			file.delete();
			flag = true;
		}
		return flag;
	}

	/**
	 * 根据路径删除指定的目录或文件，无论存在与否
	 *
	 * @param sPath
	 *            要删除的目录或文件
	 * @return 删除成功返回 true，否则返回 false。
	 */
	private static boolean deleteFolder(String sPath) {
		boolean flag = false;
		File file = new File(sPath);
		// 判断目录或文件是否存在
		if (!file.exists()) { // 不存在返回 false
			return flag;
		} else {
			// 判断是否为文件
			if (file.isFile()) { // 为文件时调用删除文件方法
				return deleteFile(sPath);
			} else { // 为目录时调用删除目录方法
				return deleteDirectory(sPath);
			}
		}
	}

	/**
	 * 封装不同的解压状态
	 * 
	 **/
	public class CompressStatus {
		public final static int START = 10000;
		public final static int HANDLING = 10001;
		public final static int COMPLETED = 10002;
		public final static int ERROR = 10003;

		public final static String PERCENT = "PERCENT";
		public final static String ERROR_COM = "ERROR";
	}

	/**
	 * 解密文件
	 * 
	 * @param sourceFile
	 *            加密文件
	 * @param decodeFile
	 *            解密文件
	 * @param b
	 *            是否删除加密文件
	 */
	public static void decodeFile(File sourceFile, File decodeFile, boolean b) {
		byte[] bs = new byte[1024];
		BufferedInputStream in = null;
		BufferedOutputStream out = null;
		try {
			in = new BufferedInputStream(new FileInputStream(sourceFile));
			out = new BufferedOutputStream(new FileOutputStream(decodeFile,
					false));
			while ((in.read(bs)) != -1) {
				for (int i = 0; i < bs.length; i++) {
					out.write(bs[i] - 1);
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			try {
				out.flush();
				in.close();
				out.close();
			} catch (Exception e2) {
				// TODO: handle exception
				e2.printStackTrace();
			}
			if (b) {
				sourceFile.delete();
			}
		}

	}

	/**
	 * 
	 * 十六进制转换字符串
	 */
	public static String hexStr2Str(String hexStr) {
		String str = "0123456789ABCDEF";
		char[] hexs = hexStr.toCharArray();
		byte[] bytes = new byte[hexStr.length() / 2];
		int n;
		for (int i = 0; i < bytes.length; i++) {
			n = str.indexOf(hexs[2 * i]) * 16;
			n += str.indexOf(hexs[2 * i + 1]);
			bytes[i] = (byte) (n & 0xff);
		}
		return new String(bytes);
	}

	public static long getZipFileSize(File path) {
		long total = -1;
		total = getZipFileSize(path.getAbsolutePath());
		return total;
	}

	public static long getZipFileSize(String path) {
		long total = -1;
		ZipInputStream zipIn = null;
		try {
			zipIn = new ZipInputStream(new FileInputStream(path));
			ZipEntry zipEntry;
			while ((zipEntry = zipIn.getNextEntry()) != null) {
				zipIn.closeEntry();
				if (!zipEntry.isDirectory()) {
					// String name = zipEntry.getName();
					// long size = zipEntry.getSize();
					// long compd = zipEntry.getCompressedSize();
					long uncomd = zipEntry.getSize();
					total += uncomd;
				}
			}
			System.out.println(" long total=" + total / 1024 / 1024);

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				zipIn.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return total;
	}

	/**
	 * 16进制加密串
	 */
	private final static String zipCharString = "377ABCAF271C00038D9BD50F000000000000000000000000000000000000";

}